#pragma once
#include"Socket.hpp"
#include"Any.hpp"
#include"Buffer.hpp"
#include"EventLoop_Timequeue.hpp"

typedef enum{
    DISCONNECTED,//连接关闭状态
    CONNECTING,//连接中状态
    CONNECTED,//连接成功状态
    DISCONNECTING,//待关闭状态
}ConnStatus;

class Connection;
using ConnPtr = std::shared_ptr<Connection>;


using ConnectedCallback = std::function<void(const ConnPtr&)>;
using MessageCallback = std::function<void(const ConnPtr&, Buffer *)>;
using ClosedCallback = std::function<void(const ConnPtr&)>;
using OtherEventCallback = std::function<void(const ConnPtr&)>;

//shared_from_this 是 C++ 标准库中的一个重要工具，用于在类的成员函数中安全地获取指向当前对象的 shared_ptr。它是 std::enable_shared_from_this 模板类提供的功能

//shared_from_this 解决了这样一个问题：当你有一个对象已经被 shared_ptr 管理，但在该对象的成员函数内部，你需要获取指向自身的 shared_ptr（而不是原始指针 this）。\
直接使用 this 创建新的 shared_ptr 会导致多个不相关的 shared_ptr 管理同一个对象，最终可能导致多次删除等问题。

class Connection : public std::enable_shared_from_this<Connection>{
    private:
        uint64_t _conn_id;// 连接的唯⼀ID，便于连接的管理和查找
        //uint64_t _timer_id;   //定时器ID，必须是唯⼀的，这块为了简化操作使⽤conn_id作为定时器ID
        int _sockfd;//连接对应的socket描述符
        bool _enable_inactive_release;//是否启用非活跃销毁(默认启用)
        bool _no_block ;//是否设置为非阻塞模式(默认设置为非阻塞模式)
        EventLoop *_loop;//连接所关联的一个EventLoop
        ConnStatus _status = CONNECTING;//连接状态(初始默认为连接中状态)
        TcpSocket _socket;//套接字操作管理
        Channel _channel;//连接的事件管理
        Buffer _input_buffer;//输入缓存
        Buffer _output_buffer;//输出缓存
        Any _context;//请求的接受处理上下⽂信息
        
        ConnectedCallback _conn_cb;//连接成功后的回调函数
        MessageCallback _msg_cb;//接收到消息后的回调函数
        ClosedCallback _closed_cb;//连接关闭后的回调函数
        OtherEventCallback _other_cb;//其他事件回调函数

        /*组件内的连接关闭回调--组件内设置的，因为服务器组件内会把所有的连接管理起来，⼀旦某个连接要关闭*/
        /*就应该从管理的地⽅移除掉⾃⼰的信息*/
        ClosedCallback _server_closed_cb;
    private:
        //这个关闭操作并⾮实际的连接释放操作，需要判断还有没有数据待处理，待发送
        void CheckAndShutdownInLoop()
        {
            _status = DISCONNECTING;//设置状态为待关闭状态
            if(_input_buffer.ReadableSize() > 0 && _msg_cb){//如果还有数据在输入缓冲区中，则继续处理读事件
                _msg_cb(shared_from_this(),&_input_buffer);
            }
            if(_output_buffer.ReadableSize() > 0){//如果还有数据在输出缓冲区中，则继续处理写事件
                if(!_channel.Writable()){//如果没有写事件监控，则添加写事件监控
                    _channel.EnableWriting();
                    _loop->ModifyEvent(&_channel);
                }
            }
            if(_output_buffer.ReadableSize() == 0){
                Release();
            }

        }
        //这个接口才是实际的释放接口
        void ReleaseInLoop()
        {
            //1. 修改连接状态，将其置为DISCONNECTED
            _status = DISCONNECTED;
            //2.移除连接的事件监控
            _loop->RemoveEvent(&_channel);
            //3.关闭socket
            _socket.Close();
            //4.关闭定时任务
            if(_loop->HasTimeTask(_conn_id)){
                _loop->CancelTimeTask(_conn_id);
            }
            //5. 调⽤关闭回调函数，避免先移除服务器管理的连接信息导致Connection被释放，再去处理会出错，因此先调⽤⽤⼾的回调函数
            if(_closed_cb){
                _closed_cb(shared_from_this());
            }
            //6.移除服务器内部管理的连接信息
            if(_server_closed_cb){
                _server_closed_cb(shared_from_this());
            }
        }

        //下面的都是设置给channel的回调函数，在channel中调⽤的
        void HandleRead()//描述符可读事件触发后调⽤的函数，接收socket数据放到接收缓冲区中，然后调⽤_message_callback
        {
            //1.从socket读取数据到缓冲区
            char buf[65536];
            ssize_t n = _socket.Recv(buf,sizeof(buf));
            if(n < 0){//出错了不能直接关闭连接
                
                CheckAndShutdownInLoop();
                return;
            }
            //n==0表⽰的是没有读取到数据，⽽并不是连接断开了，连接断开返回的是-1
            //2.将数据放⼊输⼊缓冲区,写⼊之后顺便将写偏移向后移动
            _input_buffer.Write(buf,n);
            if(_input_buffer.ReadableSize() > 0){
                _msg_cb(shared_from_this(),&_input_buffer);//shared_from_this--从当前对象⾃⾝,获取⾃⾝的shared_ptr管理对象
            }

        }
        void HandleWrite()//描述符可写事件触发后调⽤的函数，将发送缓冲区中的数据进⾏发送
        {
            char buf[65536];
            int size = _output_buffer.ReadableSize();//获取可读数据长度
            _output_buffer.ReadAndMove(buf,size);//将输出缓冲区中的数据读出到buf中，并将写偏移向后移动size个
            
            ssize_t n = _socket.Send(buf,size);//发送输出缓冲区中的数据

            if(n < 0){
                if(_input_buffer.ReadableSize() > 0){//如果还有数据在输入缓冲区中，则继续处理读事件
                    _msg_cb(shared_from_this(),&_input_buffer);
                }
                //实际关闭连接
                Release();
                return;
            }
            if(_output_buffer.ReadableSize() == 0){//如果输出缓冲区中没有数据了，则取消写事件监听
                _channel.DisableWriting();// 没有数据待发送了，关闭写事件监控
                _loop->ModifyEvent(&_channel);
                if(_status == DISCONNECTING)
                    Release();  //如果处于待关闭状态，则直接释放连接
                return;
            }

        }
        void HandleClose()/*⼀旦连接挂断了，套接字就什么都⼲不了了，因此有数据待处理就处理⼀下，完毕关闭连接*/
        {
            if(_input_buffer.ReadableSize() > 0){
                _msg_cb(shared_from_this(),&_input_buffer);
            }
            Release();
        }
        void HandleError()//出错挂断
        {
            HandleClose();
        }
        void HandleOther()
        {

        }
        void EstablishInLoop()  
        {
            // 1. 修改连接状态； 
            assert(_status == CONNECTING);//当前必须得是半连接状态
            _status = CONNECTED;
            // 2. 添加并且启动读事件监控；
            if(!_loop->HasEvent(&_channel))
                _loop->AddEvent(&_channel);
            _channel.EnableReading();
            _loop->ModifyEvent(&_channel);
            //3. 调⽤连接的回调函数
            if(_conn_cb){
                _conn_cb(shared_from_this());
            }
        }
        
        void SendInLoop(Buffer &buf)//这个接⼝并不是实际的发送接⼝，⽽只是把数据放到了发送缓冲区，启动了可写事件监控
        {
            if(_status == DISCONNECTED) return;//如果处于断开状态，则直接返回
            
            _output_buffer.Write(buf.GetReadPointer(),buf.ReadableSize());//将数据放到输出缓冲区中
            if(!_channel.Writable()){//如果没有写事件监控，则添加写事件监控
                if(!_loop->HasEvent(&_channel))
                    _loop->AddEvent(&_channel);
                _channel.EnableWriting();
                _loop->ModifyEvent(&_channel);
            }

        }

        void EnableInactiveReleaseInLoop(int sec)
        {
            _enable_inactive_release = true;
            //如果当前连接定时任务已经存在，则延时
            if(_loop->HasTimeTask(_conn_id)){
                _loop->DelayTimeTask(_conn_id);
            }
            _loop->AddTimeTask(_conn_id,sec,std::bind(&Connection::Release,this));
        }
        void DisableInactiveReleaseInLoop()
        {
            _enable_inactive_release = false;
            if(_loop->HasTimeTask(_conn_id)){
                _loop->CancelTimeTask(_conn_id);
            }
        }
        void UpgradeInLoop(const Any &context , const ConnectedCallback &conn_cb, //更新协议
                        const MessageCallback &msg_cb, 
                        const ClosedCallback &closed_cb, const OtherEventCallback &other_cb)
        {
            _context = context;
            _conn_cb = conn_cb;
            _msg_cb = msg_cb;
            _closed_cb = closed_cb;
            _other_cb = other_cb;
        }
    public:
        Connection(EventLoop *loop,uint64_t conn_id,int sockfd , bool no_block = true ,bool enable_inactive_release = false)
            :_conn_id(conn_id),
            _sockfd(sockfd),
            _socket(sockfd),
            _loop(loop),
            _channel(loop,sockfd),
            _enable_inactive_release(enable_inactive_release),
            _no_block(no_block)
        {
            if(_no_block){//设置为非阻塞模式
                NonBlock(_socket.Fd());
            }
            _channel.SetCloseCallback(std::bind(&Connection::HandleClose,this));
            _channel.SetReadCallback(std::bind(&Connection::HandleRead,this));
            _channel.SetWriteCallback(std::bind(&Connection::HandleWrite,this));
            _channel.SetErrorCallback(std::bind(&Connection::HandleError,this));
            _channel.SetOtherCallback(std::bind(&Connection::HandleOther,this));
        }
        ~Connection(){ LOG_DBG("Connection destructed");}
        int SockFd(){ return _sockfd; }
        int ConnId(){ return _conn_id; }
        bool IsConnected(){ return _status == CONNECTED; }//是否处于连接状态
        
        void SetContext(const Any& context) { _context = context; }//设置上下⽂信息
        Any *GetContext() { return &_context; }//获取上下⽂信息
        //设置回调函数
        void SetConnectedCallback(const ConnectedCallback& cb) { _conn_cb = cb; }
        void SetMessageCallback(const MessageCallback& cb) { _msg_cb = cb; }
        void SetClosedCallback(const ClosedCallback& cb) { _closed_cb = cb; }
        void SetServerClosedCallback(const ClosedCallback& cb) { _server_closed_cb = cb; }
        void SetOtherCallback(const OtherEventCallback& cb) { _other_cb = cb; }
        void Establish() //进⾏channel回调设置，启动读监控，调⽤_connected_callback
        {
            _loop->RuninLoop(std::bind(&Connection::EstablishInLoop,shared_from_this()));
        }
        void Release()
        {
            _loop->AddTaskInLoop(std::bind(&Connection::ReleaseInLoop,shared_from_this()));
        }
        void Send(const char *data, size_t len)
        {
            //外界传⼊的data，可能是个临时的空间，我们现在只是把发送操作压⼊了任务池，有可能并没有被⽴即执⾏
            //因此有可能执⾏的时候，data指向的空间有可能已经被释放了。
            Buffer buf;
            char *_data = const_cast<char *>(data);
            buf.Write(_data,len);
            _loop->RuninLoop(std::bind(&Connection::SendInLoop,this,buf));

        }

        void CheckAndShutdown()//提供给组件使⽤者的关闭接⼝--并不实际关闭，需要判断有没有数据待处理
        {
            _loop->RuninLoop(std::bind(&Connection::CheckAndShutdownInLoop,this));
        }
        void EnableInactiveRelease(int sec)//设置非活跃销毁
        {
            _loop->RuninLoop(std::bind(&Connection::EnableInactiveReleaseInLoop,this,sec)); 
        }
        void DisableInactiveRelease()//禁用非活跃销毁
        {
            _loop->RuninLoop(std::bind(&Connection::DisableInactiveReleaseInLoop,this));
        }
        void Upgrade(const Any &context , const ConnectedCallback &conn_cb, //更新协议
                        const MessageCallback &msg_cb, 
                        const ClosedCallback &closed_cb, const OtherEventCallback &other_cb)
        {
            _loop->RuninLoop(std::bind(&Connection::UpgradeInLoop,this,context,conn_cb,msg_cb,closed_cb,other_cb));
        }
};


